/**
 *  Copyright (C) 2013  Piotr Szczepa�ski
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package edu.pw.elka.gtsna.test_performance;

import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import edu.pw.elka.gtsna.centrality.betweenness.NodeBetweennessCentrality;
import edu.pw.elka.gtsna.centrality.betweenness.ProbabilisticSVNodeBetweenness;
import edu.pw.elka.gtsna.centrality.betweenness.SVNodeBetweenness;
import edu.pw.elka.gtsna.graph.Graph;
import edu.pw.elka.gtsna.graph.UndirectedGraph;
import edu.pw.elka.gtsna.graph_creator.ScaleFreeGraphGenerator;
import edu.pw.elka.gtsna.graph_evaluators.AverageDistanceEvaluator;
import edu.pw.elka.gtsna.graph_evaluators.AverageShortestPathsEvaluator;
import edu.pw.elka.gtsna.graph_evaluators.LargestComponentEvaluator;
import edu.pw.elka.gtsna.ranking.Ranking;
import edu.pw.elka.gtsna.utilities.Agregator;
import edu.pw.elka.gtsna.utilities.MathPowers;
import edu.pw.elka.gtsna.utilities.RandomSubset;

/**
 * @author Piotr Lech Szczepa�ski
 * @author P.Szczepanski@ii.pw.edu.pl 
 *
 */
public class RandomFailurePerformer extends Performer {

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		
		
		if (args.length < 7) {
			System.out.println("Missing arguments: "+"GRAPH_SIZE, a, bStep, bLimit, iterations, threads, graphs");
			return;
		}

	    final int graphSize = Integer.parseInt(args[0]) ;
	   // int a = Integer.parseInt(args[1]) ;
	    final int bStep = Integer.parseInt(args[2]) ;
	    final int bLimit = Integer.parseInt(args[3]) ;
	    final int iterations = Integer.parseInt(args[4]) ;
	    final int threads = Integer.parseInt(args[5]) ;
	    final int graphs = Integer.parseInt(args[6]) ;
	    
	    final Agregator[] distancesB = new Agregator[graphSize+1];
	    final Agregator[] distancesSV = new Agregator[graphSize+1];
	    final Agregator[] componentsB = new Agregator[graphSize+1];
	    final Agregator[] componentsSV = new Agregator[graphSize+1];
	    for (int i=0; i<=graphSize; i++){
	    	distancesB[i]= new Agregator();
	    	distancesSV[i]= new Agregator();
	    	componentsB[i]= new Agregator();
	    	componentsSV[i]= new Agregator();
	    }
		  
		  for (int ii=1; ii<=graphs; ii++) {
			  System.out.println(ii+"/"+graphs);
		  
			//final int graphSize = 150;
			//final int a = 1;
			//final int iterations = 5000;
		
			final Graph gr = new UndirectedGraph(new ScaleFreeGraphGenerator(graphSize,(int)Math.sqrt(graphSize/2)));
			AverageDistanceEvaluator aD = new AverageDistanceEvaluator(gr);
			final double mainD = aD.evaluate();
			
			LargestComponentEvaluator aS = new LargestComponentEvaluator(gr);
			final double mainP = aS.evaluate();
			
			NodeBetweennessCentrality nB = new NodeBetweennessCentrality(gr);
			nB.computeCentrality();
			final Ranking<Integer> rB = new Ranking<Integer>(nB.getCentralities());
			
			MathPowers.precompute(gr.getNumOfNodes()+1);
			
			executorService = Executors.newFixedThreadPool(threads);
			
		    final Semaphore available = new Semaphore(threads);
			
			for (int a=graphSize-1;a>0; a-=1){
			
				available.acquire();
				final int b2 = graphSize;
				final int a2 = a;
				executorService.execute(new Runnable() {
				    public void run() {


						ProbabilisticSVNodeBetweenness PSV = new ProbabilisticSVNodeBetweenness(gr){
							@Override
							protected double distribution(int k){
								if (k>=a2 && k<b2)
									return 1.0/(b2-a2);
								else return 0.0;
							}
						};
						
						PSV.computeCentrality();
						Ranking<Integer> rPSV = new Ranking<Integer>(PSV.getCentralities()); 
						
						//Ranking.displayRankings(r1, r2);
						
						double Bdist  = 0.0;
						double Bpaths = 0.0;
						
						double SVdist  = 0.0;
						double SVpaths = 0.0;
						
				
						
						RandomSubset rs = new RandomSubset(gr.getAllNodes());
						rs.setIterationsLimitation(iterations);
						rs.setSize(rand.nextInt((b2-a2))+a2);
						double i = 0.0;
						for(Set<Integer> failers: rs){ i++;
							//System.out.println(i+"/"+1000+" "+nf.format(Bdist/(i-1))+" vs "+nf.format(SVdist/(i-1))+"      "+nf.format(Bpaths/(i-1))+" vs "+nf.format(SVpaths/(i-1))); i++;
							Graph grB = new UndirectedGraph(gr);
							Graph grSV = new UndirectedGraph(gr);
							
							for(int node: failers){
								if ( ( (((double)rB.getPosition(node)*(double)rB.getPosition(node))/(gr.getNumOfNodes()*gr.getNumOfNodes())) > rand.nextDouble()) ){
									grB.isolateNode(node);
								}
								
								if ( ( (((double)rPSV.getPosition(node)*(double)rPSV.getPosition(node))/(gr.getNumOfNodes()*gr.getNumOfNodes())) > rand.nextDouble()) ){
									grSV.isolateNode(node);
								}
				
							}
							AverageDistanceEvaluator dB = new AverageDistanceEvaluator(grB);
							AverageDistanceEvaluator dSV = new AverageDistanceEvaluator(grSV);
							LargestComponentEvaluator pB = new LargestComponentEvaluator(grB);
							LargestComponentEvaluator pSV = new LargestComponentEvaluator(grSV);
							Bdist += (mainD-dB.evaluate());
							SVdist  += (mainD-dSV.evaluate());
							
							Bpaths += (mainP-pB.evaluate());
							SVpaths += (mainP-pSV.evaluate());
				
							rs.setSize(rand.nextInt((b2-a2))+a2);
							
							
						}
						//System.out.println("("+a+","+b2+") "+nf.format(Bdist/iterations)+" vs "+nf.format(SVdist/iterations)+"      "+nf.format(Bpaths/iterations)+" vs "+nf.format(SVpaths/iterations));
						distancesB[a2].observe(Bdist/iterations);
						distancesSV[a2].observe(SVdist/iterations);
						componentsB[a2].observe(Bpaths/iterations);
						componentsSV[a2].observe(SVpaths/iterations);
						
						available.release();
				    }
				});
				
			}
			
			executorService.shutdown();

			
			try {
					executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
				} catch (InterruptedException e) {
					System.out.println("ERROR: PROCESS INTERUPTED");
				}
			
		  }
		  
		  
		for (int i=1; i<=graphSize-1; i++){
			System.out.println(i+" distancesB: "+sixDForm.format(distancesB[i].getMean())+" "+sixDForm.format(distancesB[i].getLowerBound(75))+" "+sixDForm.format(distancesB[i].getUpperBound(75)));
			System.out.println(i+" distancesSV: "+sixDForm.format(distancesSV[i].getMean())+" "+sixDForm.format(distancesSV[i].getLowerBound(75))+" "+sixDForm.format(distancesSV[i].getUpperBound(75)));
			System.out.println(i+" componentsB: "+sixDForm.format(componentsB[i].getMean())+" "+sixDForm.format(componentsB[i].getLowerBound(75))+" "+sixDForm.format(componentsB[i].getUpperBound(75)));
			System.out.println(i+" componentsSV: "+sixDForm.format(componentsSV[i].getMean())+" "+sixDForm.format(componentsSV[i].getLowerBound(75))+" "+sixDForm.format(componentsSV[i].getUpperBound(75)));
		}
		  
		}
}
